﻿Option Explicit On
Option Strict On

Imports TreeCreeper01Interfaces

Friend Class cTaxonomicDistanceLookupTable

    Implements ITaxonomicDistanceLookupTable

    Private mInterdistancesBaseZero(,) As Single
    Private mSpeciesIndicesByID As Dictionary(Of String, Integer)
    Private mSpeciesIndicesByName As Dictionary(Of String, Integer)
    Private mSpeciesNamesBaseZero As List(Of String)
    Private mCountOfSpecies As Integer
    Private mChecked As Boolean

    Public Sub New(ByVal SpeciesCollection As ITaxonomicSpeciesCollection)

        Dim SpeciesIndex As Integer
        Dim Species As ITaxonomicSpecies
        Dim I As Integer
        Dim J As Integer

        mCountOfSpecies = SpeciesCollection.Count

        ReDim mInterdistancesBaseZero(mCountOfSpecies - 1, mCountOfSpecies - 1)

        mSpeciesIndicesByID = New Dictionary(Of String, Integer)
        mSpeciesIndicesByName = New Dictionary(Of String, Integer)
        mSpeciesNamesBaseZero = New List(Of String)

        For I = 0 To mCountOfSpecies - 1
            For J = 0 To mCountOfSpecies - 1
                mInterdistancesBaseZero(I, J) = -1
            Next
        Next

        With SpeciesCollection
            For SpeciesIndex = 1 To .Count
                Species = .TaxonomicSpeciesByIndexBaseOne(SpeciesIndex)

                ' 2012-10-12 Vesa Kivistö: bad input file format might have duplicates, let's build good error reporting for this
                If mSpeciesNamesBaseZero.Contains(Species.Name) Then
                    Throw New Exception("Duplicate species name '" + Species.Name + "' encountered in SNBZ data container. Treecreeper can't continue. Sorry. Most likely the input file is not in proper format.")
                Else
                    mSpeciesNamesBaseZero.Add(Species.Name)
                End If

                If mSpeciesIndicesByID.ContainsKey(Species.UniqueKey) Then
                    Throw New Exception("Duplicate species unique key '" + Species.UniqueKey + "' encountered. Treecreeper can't continue. Sorry. Most likely the input file is not in proper format.")
                Else
                    mSpeciesIndicesByID.Add(Species.UniqueKey, SpeciesIndex)
                End If

                If mSpeciesIndicesByName.ContainsKey(Species.Name) Then
                    Throw New Exception("Duplicate species name '" + Species.Name + "' encountered in SIBN data container. Treecreeper can't continue. Sorry. Most likely the input file is not in proper format.")
                Else
                    mSpeciesIndicesByName.Add(Species.Name, SpeciesIndex)
                End If

            Next
        End With

    End Sub

    Private Sub SetInterSpeciesDistance(ByVal SpeciesOneName As String, ByVal SpeciesTwoName As String, ByVal Distance As Single) Implements TreeCreeper01Interfaces.ITaxonomicDistanceLookupTable.SetInterSpeciesDistance

        'The source override table may be much larger than the study sample population, and contain many unnecessary species. 
        'We just ignore information about species which don't appear in the study taxonomy tree.

        Dim SpeciesOneIndex As Integer
        Dim SpeciesTwoIndex As Integer
        
        With mSpeciesIndicesByName
            If .ContainsKey(SpeciesOneName) Then
                SpeciesOneIndex = .Item(SpeciesOneName) - 1
                If .ContainsKey(SpeciesTwoName) Then
                    SpeciesTwoIndex = .Item(SpeciesTwoName) - 1
                    mInterdistancesBaseZero(SpeciesOneIndex, SpeciesTwoIndex) = Distance
                    'Debug.Print("Assigned direct distance between " & SpeciesOneName & " and " & SpeciesTwoName & " at " & SpeciesOneIndex.ToString & "," & SpeciesTwoIndex.ToString & ": value " & Distance.ToString)
                End If
            End If
        End With
        
    End Sub

    Private Sub CheckIntegrity()

        'Check that each point in the array has an assigned value (not the -1 default initialised value)
        'So the value can be zero, of course.

        Dim SpeciesOneIndex As Integer
        Dim SpeciesTwoIndex As Integer
        Dim SpeciesName(2) As String

        If Not mChecked Then
            For SpeciesOneIndex = 0 To mCountOfSpecies - 1
                For SpeciesTwoIndex = 0 To mCountOfSpecies - 1
                    If Not mInterdistancesBaseZero(SpeciesOneIndex, SpeciesTwoIndex) >= 0 Then
                        SpeciesName(0) = mSpeciesNamesBaseZero(SpeciesOneIndex)
                        SpeciesName(1) = mSpeciesNamesBaseZero(SpeciesTwoIndex)
                        Throw New Exception("The look-up table is not complete: " & SpeciesName(0) & " versus " & SpeciesName(1) & ".")
                    End If
                Next
            Next
            mChecked = True
        End If

    End Sub

    Public Property TaxonomicDistanceBetweenSpecies(ByVal FirstSpecies As ITaxonomicSpecies, ByVal SecondSpecies As ITaxonomicSpecies) As Single

        Get

            Dim SpeciesOneIndex As Integer
            Dim SpeciesTwoIndex As Integer
            Dim Interdistance As Single

            CheckIntegrity()

            SpeciesOneIndex = mSpeciesIndicesByID(FirstSpecies.UniqueKey) - 1
            SpeciesTwoIndex = mSpeciesIndicesByID(SecondSpecies.UniqueKey) - 1

            Interdistance = mInterdistancesBaseZero(SpeciesOneIndex, SpeciesTwoIndex)
            Return Interdistance


        End Get

        Set(ByVal Interdistance As Single)

            Dim SpeciesOneIndex As Integer
            Dim SpeciesTwoIndex As Integer

            SpeciesOneIndex = mSpeciesIndicesByID(FirstSpecies.UniqueKey) - 1
            SpeciesTwoIndex = mSpeciesIndicesByID(SecondSpecies.UniqueKey) - 1

            mInterdistancesBaseZero(SpeciesOneIndex, SpeciesTwoIndex) = Interdistance
            mInterdistancesBaseZero(SpeciesTwoIndex, SpeciesOneIndex) = Interdistance

        End Set

    End Property


End Class
